package main

import (
    "encoding/hex"
    "fmt"
    "net"
    "time"
)

// 编码数据为 ASCII 码表示的十六进制
func encodeToASCIIHex(data []byte) []byte {
    encoded := make([]byte, hex.EncodedLen(len(data)))
    hex.Encode(encoded, data)
    return encoded
}

// 计算 LENID 和 LCHKSUM，返回 LENGTH 字段
func calculateLength(info []byte) (uint16, error) {
    lenid := len(info) * 2 // INFO 实际传输的 ASCII 字节数

    // 分别获取 LENID 的 D11D10D9D8, D7D6D5D4, D3D2D1D0 三组比特并求和
    d11d8 := (lenid >> 8) & 0x0F
    d7d4 := (lenid >> 4) & 0x0F
    d3d0 := lenid & 0x0F
    lchksum := byte((d11d8 + d7d4 + d3d0) % 16) // 模 16 求余数
    lchksum = ^lchksum + 1                       // 取反加 1

    length := uint16(lchksum)<<8 | uint16(lenid) // LENGTH = LCHKSUM 高字节 | LENID 低字节
    return length, nil
}

// 构建协议帧
func buildProtocolFrame(adr, cid1, cid2 byte, info []byte) ([]byte, error) {
    soi := byte(0x7E)                          // 起始位标志，固定为 0x7E
    ver := byte(0x21)                          // 协议版本，固定为 0x21
    length, err := calculateLength(info)       // 计算 LENGTH 字段
    if err != nil {
        return nil, fmt.Errorf("计算 LENGTH 字段失败: %v", err)
    }
    eoi := byte(0x0D)                          // 结束码，固定为 0x0D

    // 校验和计算包括 ver, adr, cid1, cid2, length, 和 info
    rawData := []byte{ver, adr, cid1, cid2, byte(length >> 8), byte(length)}
    rawData = append(rawData, info...)
    chksum := calculateChecksum(rawData)

    // 构建帧并转换为 ASCII 表示的十六进制格式
    frame := []byte{soi}
    frame = append(frame, encodeToASCIIHex([]byte{ver, adr, cid1, cid2, byte(length >> 8), byte(length)})...)
    frame = append(frame, encodeToASCIIHex(info)...)
    frame = append(frame, encodeToASCIIHex([]byte{byte(chksum >> 8), byte(chksum & 0xFF)})...)
    frame = append(frame, eoi)

    return frame, nil
}

// 计算校验和
func calculateChecksum(data []byte) uint16 {
    var sum uint16
    for _, b := range data {
        sum += uint16(b)
    }
    return ^sum + 1
}

// 发送和接收协议帧
func communicateWithSM(conn net.Conn, adr, cid1, cid2 byte, info []byte) error {
    frame, err := buildProtocolFrame(adr, cid1, cid2, info)
    if err != nil {
        return fmt.Errorf("构建协议帧失败: %v", err)
    }

    // 发送协议帧
    _, err = conn.Write(frame)
    if err != nil {
        return fmt.Errorf("发送协议帧失败: %v", err)
    }
    fmt.Println("发送的协议帧:", frame)

    // 设置读取超时并接收响应
    conn.SetReadDeadline(time.Now().Add(2 * time.Second))
    response := make([]byte, 256)
    n, err := conn.Read(response)
    if err != nil {
        return fmt.Errorf("接收响应失败: %v", err)
    }

    // 假设返回码在 CID2 位置
    rtn := response[4]
    fmt.Printf("接收到的响应返回码: 0x%X\n", rtn)

    return nil
}

func main() {
    targetAddr := "192.168.1.100:5020" // 假设设备 IP 和端口

    conn, err := net.Dial("tcp", targetAddr)
    if err != nil {
        fmt.Println("连接失败:", err)
        return
    }
    defer conn.Close()

    adr, cid1, cid2 := byte(0x01), byte(0x10), byte(0x20)
    info := []byte{0x30, 0x40} // 示例信息内容

    if err := communicateWithSM(conn, adr, cid1, cid2, info); err != nil {
        fmt.Println("通信失败:", err)
    }
}
